/**
 * User: Siemen
 * Date: 2011-2-26
 * Time: 10:17:46
 * To change this template use File | Settings | File Templates.
 */
var GemUnit;
GemUnit = {
    // ===== 公共存放空间
    /** 所有宝石 @type {Array} */
    allGems: [],
    /** 待爆炸宝石 @type {Array} */
    boomGems: [],
    /** 等待爆炸检查的宝石 @type {Array} */
    waitBoomGems: [],
    /** 正在旋转的宝石 @type {GemUnit} */
    playingGem: null,
    /** 面板坐标 用于计算宝石所在行列的坐标 @type {Object} */
    boardPos: {x:261, y:38},

    // ===== 默认值
    /** 宽度 @type {number} */
    width: 64,
    /** 高度 @type {number} */
    height: 64,

    // ===== 宝石图片
    /** 宝石图片起始坐标X @type {number} */
    imageX: 0,
    /** 宝石图片起始坐标Y @type {number} */
    imageY: 0,
    /** 宝石图片显示宽度 @type {number} */
    imageWidth: 64,
    /** 宝石图片显示高度 @type {number} */
    imageHeight: 64,
    /** 宝石图片sprit行数 */
    imageRowLimit: 4,
    /** 宝石图片sprit列数 */
    imageColumnLimit: 5,

    // ===== 枚举值
    /** 宝石固定的颜色枚举 @enum */
    colors: ['gem.blue', 'gem.green', 'gem.orange',
        'gem.purple', 'gem.red', 'gem.white', 'gem.yellow'],
    /** 拖动方向枚举 @enum */
    dragDirect: {
        left:{name: 'left', value: -1},
        right:{name: 'right', value: 1},
        up:{name: 'up', value: -1},
        down:{name: 'down', value: 1}
    },

    // ===== 标志位
    /**
     * 是否正在旋转 标志位
     * 次值为false并不能认为宝石已经停止旋转
     * isStop用于判断宝石是否已经处于停止旋转状态
     * 此标志位仅用于宝石行为的开关
     * @type {boolean}
     */
    isPlaying: false,
    /**
     * 是否已经静止 标志位
     * 转动已经完全结束
     * @type {boolean}
     * */
    isStop: true,
    /**
     * 是否是返回移动
     * 返回移动是指移动宝石后发现没有匹配爆炸条件而返回的动作
     * 该标志位指示本次移动是否是返回移动
     * @type {boolean}
     */
    isMoveBack: false,
    /** 是否正在拖动 @type {boolean} */
    isDragging: false,
    /** 是否重绘完成 */
    isReShowing: false,
    /**
     * 从this.colors中随机选出一种枚举值
     * @static 类静态方法
     * @return {string} 颜色值
     */
    randomColor: function () {
        return this.colors[Math.floor(Math.random() * this.colors.length)];
    },
    /**
     * 验证宝石索引是否有效
     * @static
     * @param row {number}
     * @param col {number}
     * @param directName {string} 方向 [option]
     * @return {boolean}
     */
    validGemIndex: function(row, col, directName) {
        return (row >= 0 && row < 8 && col >= 0 && col < 8) ||
                (directName === 'right' && index.c % 8 !== 0);
    },
    /**
     * 交换两颗宝石的信息
     * @static
     */
    switchGemInfo: function (gem1, gem2) {
        var gems = XM.canvas.LayerManager.getItem('gemLayer');
        var temp = gems[gem1.row][gem1.column];
        gems[gem1.row][gem1.column] = gem2;
        gems[gem2.row][gem2.column] = temp;

        var tR = gem1.row, tC = gem1.column;
        gem1.row = gem2.row,gem1.column = gem2.column;
        gem2.row = tR,gem2.column = tC;
    },
    /**
     * 生成宝石名称
     * @static
     * @param r {number} 宝石所在行
     * @param c {number} 宝石所在列
     * @return {string} 宝石名称
     */
    generateName: function (r, c) {
        return 'gem-' + r + '-' + c + '-' + new Date().getTime();
    },
    /**
     * 宝石爆炸
     * @static
     * @param gemThis
     * @param gemOther
     */
    doBoom: function(gems) {
        var boomGems = [];
        for (var i = 0, len = gems.length; i < len; i++) {
            boomGems = boomGems.concat(gems[i].getBoomGems());
        }
        // 去除重复宝石
        for (var i = 0, len = boomGems.length; i < len; i++) {
            var gem = boomGems[i];
            if (!gem.isBelongTo(GemUnit.boomGems)) {
                GemUnit.boomGems.push(gem);
            }
        }
        //GemUnit.boomGems = GemUnit.boomGems.concat(boomGems);
        //console.log('boom', GemUnit.boomGems);

        if (boomGems.length > 0) {
            for (var i = 0, len = boomGems.length; i < len; i++) {
                boomGems[i].hideBoomGems();
            }
            return true;
        }

        return false;
    },
    /**
     * 转动
     * @param callback {Function}
     */
    play: function (callback) {
        if (GemUnit.playingGem)
            GemUnit.playingGem.isPlaying = false;
        
        this.isPlaying = true;
        this.isStop = false;
        GemUnit.playingGem = this;
        this._play(callback);
    },
    /**
     * 转动
     * @private
     * @param callback 转动回调
     */
    _play: function (callback) {
        var si = setInterval((function (_self) {
            return function () { //console.log('print');
                if (!_self._setNextBackgroundPosition()) {
                    callback && callback();
                    if (!_self.isPlaying) { //console.log('end');
                        _self.isStop = true;
                        clearInterval(si);
                    }
                }
            }
        })(this), GLOBAL_CONFIG['global'].paintMillisecond);
    },
    /**
     * 下一帧背景
     * @private
     * @return {boolean}
     */
    _setNextBackgroundPosition: function () {
        if (this.imageColumn < this.imageColumnLimit - 1)
            this.imageColumn += 1;
        else if (this.imageColumn === this.imageColumnLimit - 1 && this.imageRow < this.imageRowLimit - 1)
            this.imageRow += 1,this.imageColumn = 0;

        if (this.imageColumn === this.imageColumnLimit - 1 && this.imageRow === this.imageRowLimit - 1) {
            this.imageColumn = 0,this.imageRow = 0;
            this.isImageLast = true;
        }
        this.imageX = this.imageColumn * GemUnit.width;
        this.imageY = this.imageRow * GemUnit.height;

        if (this.isImageLast) {
            this.isImageLast = false;
            return false;
        } else {
            return true;
        }
    },
    /**
     * 获得拖动方向
     * @param ev {Object}
     * @return {Object}
     */
    getDirect: function (ev) {
        var dragDirect, MDE = XM.canvas.EventTranslator.mouseDownEvent;

        // right
        if (ev.clientX - MDE.clientX > GLOBAL_CONFIG['global'].dragOffset)
            dragDirect = this.dragDirect.right;
        // left
        else if (ev.clientX - MDE.clientX < -GLOBAL_CONFIG['global'].dragOffset)
            dragDirect = this.dragDirect.left;
        // down
        else if (ev.clientY - MDE.clientY > GLOBAL_CONFIG['global'].dragOffset)
            dragDirect = this.dragDirect.down;
        // up
        else if (ev.clientY - MDE.clientY < -GLOBAL_CONFIG['global'].dragOffset)
            dragDirect = this.dragDirect.up;

        return dragDirect;
    },
    /**
     * 获得目标宝石
     * 从拖动事件对象中获得目标宝石
     * @param ev {Object}
     * @return GemUnit
     */
    getTargetGem: function (ev) {
        var index = {r: -1, c: -1};
        var d = this.getDirect(ev);
        switch (d.name) {
            case 'left':
            case 'right':
                index.r = this.row,index.c = this.column + d.value;
                break;
            case 'up':
            case 'down':
                index.r = this.row + d.value,index.c = this.column;
                break;
            default:
                throw new Error('invalid direct: ' + d.name);
                break;
        }
        if (!GemUnit.validGemIndex(index.r, index.c, d.name)) return null;
        return XM.canvas.LayerManager.getItem('gemLayer')[index.r][index.c];
    },
    /**
     * 停止转动
     */
    stop: function(gem) {
        gem.isPlaying = false;
        if (this.isStop) this.play();
        this.isPlaying = false;
    },
    /**
     * 返回移动
     * @param gem {GemUnit} 目标宝石
     */
    moveBack: function (gem) {
        GemUnit.isMoveBack = true;
        this.moveToGem(gem);
    },
    /**
     * 宝石to宝石移动
     * @param gem {GemUnit} 目标宝石
     */
    moveToGem: function (gem) {
        this.stop(gem);

        var switched = false;
        this.isMoving = gem.isMoving = true;
        if (this.row < gem.row || this.column < gem.column) {
            this.switchGemInfo(this, gem);
            switched = true;
        }

        // move gems
        var si = setInterval((function (_self, gem, switched) {
            var selfInfo = {x: _self.x, y: _self.y},
                    gemInfo = {x: gem.x, y: gem.y},
                    finished = false, step = 4;

            return function () {
                if (finished) {
                    clearInterval(si);
                    if (!switched) {
                        _self.switchGemInfo(_self, gem);
                    }
                    var selfPos = _self.syncPosition();
                    var gemPos = gem.syncPosition();
                    _self.x = selfPos.x,_self.y = selfPos.y;
                    gem.x = gemPos.x,gem.y = gemPos.y;
                    _self.isMoving = gem.isMoving = false;

                    if (!GemUnit.isMoveBack) {
                        // TODO: trigger move event
                        XM.canvas.EventTranslator.triggerItem(_self, 'move', gem);
                    } else {
                        GemUnit.isMoveBack = false;
                    }
                } else if (selfInfo.x !== gemInfo.x) {
                    if (selfInfo.x < gemInfo.x) {
                        _self.x += step;
                        gem.x -= step;
                        if (_self.x >= gemInfo.x - step) finished = true;
                    } else {
                        _self.x -= step;
                        gem.x += step;
                        if (_self.x <= gemInfo.x + step) finished = true;
                    }
                } else if (selfInfo.y !== gemInfo.y) {
                    if (selfInfo.y < gemInfo.y) {
                        _self.y += step;
                        gem.y -= step;
                        if (_self.y >= gemInfo.y - step) finished = true;
                    } else {
                        _self.y -= step;
                        gem.y += step;
                        if (_self.y <= gemInfo.y + step) finished = true;
                    }
                }

            };
        })(this, gem, switched), GLOBAL_CONFIG['global'].paintMillisecond);
    },
    /**
     * 该宝石的周围是否有宝石正在转动
     * @return {boolean}
     */
    isNearPlaying: function() {
        var nearGems = this.getNearGems();
        for (var p in nearGems) {
            var gem = nearGems[p];
            if (!gem) continue;
            if (!gem.isStop && !gem.isMoving) {
                return gem;
            }
        }

        return null;
    },
    /**
     * 获得附近宝石
     * return {Object}
     */
    getNearGems: function() {
        var nearGems = {u:null,d:null,l:null,r:null};

        var gemIndexes = {
            u:{row: this.row - 1, col: this.column}, // up
            d:{row: this.row + 1, col: this.column}, // down
            l:{row: this.row, col: this.column - 1}, // left
            r:{row: this.row, col: this.column + 1} // right
        };

        for (var p in gemIndexes) {
            var index = gemIndexes[p];
            if (!GemUnit.validGemIndex(index.row, index.col)) continue;

            nearGems[p] = GemUnit.allGems[index.row][index.col];
        }
        return nearGems;
    },
    /**
     * 同步宝石行列坐标
     * @return {Object}
     */
    syncPosition: function() {
        return {y:this.boardPos.y + GemUnit.height * this.row,
            x:this.boardPos.x + GemUnit.width * this.column};
    },
    /**
     * 获得爆炸宝石
     * @return {Array}
     */
    getBoomGems: function() {
        var chainGems = this.getChainGems();
        var boomGems = [], hasBoom = false;
        var chainUD = chainGems.u.length + chainGems.d.length;
        var chainLR = chainGems.l.length + chainGems.r.length;

        if (chainUD >= 2 || chainLR >= 2) {
            if (chainUD >= 2) {
                boomGems = boomGems.concat(chainGems.u, chainGems.d);
                hasBoom = true;
            }

            if (chainLR >= 2) {
                boomGems = boomGems.concat(chainGems.l, chainGems.r);
                hasBoom = true;
            }

            if (hasBoom) {
                boomGems = boomGems.concat(this);
            }
        }

        return boomGems;
    },
    /**
     * 该宝石是否属于参数中的宝石
     * @param gems {Array}
     */
    isBelongTo: function(gems) {
        for (var i = 0, len = gems.length; i < len; i++) {
            //console.log(this.resourceID, gems[i].resourceID);
            if (this.name === gems[i].name) {
                return true;
            }
        }

        return false;
    },
    /**
     * 需要获得offset和该列顶部剩余宝石
     * 用offset设置剩余宝石
     * @return {Object}
     */
    getDroppingInfo: function(boomGems) {
        var cBox = [];

        // 计算得出该列爆炸的最底端和最顶端的宝石
        function setGem(gem) {
            var has = false;
            for (var i = 0, len = cBox.length; i < len; i++) {
                var col = cBox[i];
                if (gem.column === col.bottomGem.column) {
                    has = true;
                    col.boomGems.push(gem);
                    if (gem.row < col.topGem.row) {
                        col.topGem = gem;
                    }

                    if (gem.row > col.bottomGem.row) {
                        col.bottomGem = gem;
                    }
                    break;
                }
            }

            if (!has) {
                cBox.push({
                    topGem: gem,
                    bottomGem: gem,
                    boomGems: [gem]
                });
            }
        }

        for (i = 0, len = boomGems.length; i < len; i++) {
            setGem(boomGems[i]);
        }

        // 计算出offset
        for (i = 0, len = cBox.length; i < len; i++) {
            col = cBox[i];
            col.offset = col.bottomGem.row - col.topGem.row + 1;
        }

        // 得到每列顶上剩余宝石
        for (var i = 0, iLen = cBox.length; i < iLen; i++) {
            var col = cBox[i], column = col.topGem.column;
            col.leftGems = [];
            for (var j = col.topGem.row - 1; j >= 0; j--) {
                col.leftGems.push(GemUnit.allGems[j][column]);
            }
        }

        // 交换数组序列
        /*
         for (var i = 0, iLen = cBox.length; i < iLen; i++) {
         var col = cBox[i], column = col.topGem.column;
         for (var j = 0, jLen = col.leftGems.length; i < jLen; j++) {
         var gem = col.leftGems[j];
         console.log(gem.row, col.offset, column);
         GemUnit.allGems[gem.row - col.offset][column] = gem;

         var tGem = XM.lang.clone(GemUnit);
         tGem.width = 0, tGem.height = 0;
         GemUnit.allGems[gem.row][column] = tGem;
         gem.row -= col.offset;
         }
         }
         */

        return cBox;
    },
    changeColor: function(c) {
        var color = c || GemUnit.randomColor();
        this.resourceID = color;
        this.image = XM.common.ResourceManager.getResource(this.resourceID);
        return this.resourceID;
    },
    /**
     * 宝石隐藏完成后的操作
     */
    hideFinish: function() {
        for (i = 0, len = GemUnit.boomGems.length; i < len; i++) {
            if (!GemUnit.boomGems[i].isHide) {
                return;
            }
        }

        var boomDropInfo = GemUnit.getDroppingInfo(GemUnit.boomGems);

        // 先安顿好爆炸的宝石，给他们一个临时的位置
        var tempPlace = [[],[],[],[],[],[],[],[]];

        for (i = 0, len = boomDropInfo.length; i < len; i++) {
            var colInfo = boomDropInfo[i],
                    leftGems = colInfo.leftGems,
                    boomGems = colInfo.boomGems;

            // 将爆炸宝石的信息作更改 并放入临时数组中
            // 临时数组中的索引是新的索引位置
            for (var j = 0, jLen = boomGems.length; j < jLen; j++) {
                var boomGem = boomGems[j],
                        tRow = boomGem.row - leftGems.length,
                        tColumn = boomGem.column;

                tempPlace[tRow][tColumn] = boomGem;
                boomGem.changeColor();
                boomGem.row = tRow, boomGem.column = tColumn;
            }

            // 改变剩余宝石信息
            for (var j = 0, jLen = leftGems.length; j < jLen; j++) {
                var leftGem = leftGems[j],
                        tRow = leftGem.row + boomGems.length,
                        tColumn = leftGem.column;

                GemUnit.allGems[tRow][tColumn] = leftGem;
                leftGem.row = tRow, leftGem.column = tColumn;
            }

            // 将临时位置的爆炸宝石填入allGems中的新位置
            for (var j = 0, jLen = boomGems.length; j < jLen; j++) {
                var boomGem = boomGems[j];
                GemUnit.allGems[boomGem.row][boomGem.column] = tempPlace[boomGem.row][boomGem.column];
            }
        }

        //console.log('tempPlace:', tempPlace);
        //console.log('allGems:', GemUnit.allGems);
        //console.log('boardPos:', GemUnit.boardPos.x, GemUnit.boardPos.y);

        for (var i = 0, len = boomDropInfo.length; i < len; i++) {
            var colInfo = boomDropInfo[i],
                    leftGems = colInfo.leftGems,
                    boomGems = colInfo.boomGems;

            for (var j = 0, jLen = leftGems.length; j < jLen; j++) {
                var leftGem = leftGems[j];
                leftGem.syncDrop();
            }

            for (j = 0, jLen = boomGems.length; j < jLen; j++) {
                var boomGem = boomGems[j];
                boomGem.reShow();
            }
        }
        GemUnit.waitBoomGems = [];
        GemUnit.boomGems = [];

        // 再次判断

        var needGems = [];
        for (var i = 0, iLen = boomDropInfo.length; i < iLen; i++) {
            var info = boomDropInfo[i], leftGems = info.leftGems, boomGems = info.boomGems;
            needGems = needGems.concat(leftGems).concat(boomGems);
        }
        GemUnit.waitBoomGems = needGems;
    },
    boomAgain: function() { console.log('boomagain');
        var wGems = GemUnit.waitBoomGems;
        for (var i = 0, len = wGems.length; i < len; i++) {
            var gem = wGems[i];
            if (gem.isMoving || gem.isDragging) return;
        }

        GemUnit.doBoom(wGems);
    },
    /**
     * 宝石重新显示
     * @param x {number}
     * @param y {number}
     */
    reShow: function() { console.log('reshow', this.row, this.column);
        this.isHide = false;
        var reShowPos = this.syncPosition();
        this.x = reShowPos.x + GemUnit.width / 2, this.y = reShowPos.y + GemUnit.height / 2;

        // 宝石重现
        var si = setInterval((function(_self) {
            var showWHOffset = GLOBAL_CONFIG['global'].GemUnit.showWHOffset
            var showXYOffset = GLOBAL_CONFIG['global'].GemUnit.showXYOffset
            return function() {
                _self.width += showWHOffset;
                _self.height += showWHOffset;
                _self.x -= showXYOffset;
                _self.y -= showXYOffset;

                if (_self.width >= GemUnit.width - showWHOffset) {
                    clearInterval(si);
                    _self.isMoving = false;
                    _self.boomAgain();

                    var selfPos = _self.syncPosition();
                    _self.x = selfPos.x, _self.y = selfPos.y;
                    _self.width = GemUnit.width;
                    _self.height = GemUnit.height;
                }
            }
        })(this), GLOBAL_CONFIG['global'].paintMillisecond);
    },
    dropToPos: function(x, y) {
        GemUnit.isDropping = true;
        this.isMoving = true;
        var si = setInterval((function(_self) {
            return function() {
                _self.y += GLOBAL_CONFIG['global'].GemUnit.dropOffset;
                if (_self.y > y - GLOBAL_CONFIG['global'].GemUnit.dropOffset) {
                    _self.y = y;
                    clearInterval(si);
                    GemUnit.isDropping = false;
                    _self.isMoving = false;
                    _self.boomAgain();
                }
            }
        })(this), GLOBAL_CONFIG['global'].paintMillisecond);
    },
    syncDrop: function() {
        var pos = this.syncPosition();
        this.dropToPos(pos.x, pos.y);
    },
    hideBoomGems: function() {
        this.isMoving = true;

        // 宝石消失
        var si = setInterval((function(_self){
            var hideWHOffset = GLOBAL_CONFIG['global'].GemUnit.hideWHOffset;
            var hideXYOffset = GLOBAL_CONFIG['global'].GemUnit.hideXYOffset;
            return function() {
                _self.width -= hideWHOffset;
                _self.height -= hideWHOffset;
                _self.x += hideXYOffset;
                _self.y += hideXYOffset;
                if (_self.width <= hideWHOffset) {
                    _self.width = _self.height = 1;
                    _self.isHide = true; console.log('hide', _self.row, _self.column);
                    clearInterval(si);
                    _self.hideFinish();
                }
            }
        })(this), GLOBAL_CONFIG['global'].paintMillisecond);
    },
    equals: function(gem) {
        if (!gem) return false;
        return this.resourceID === gem.resourceID;
    },
    /**
     * 获得宝石链
     * @return {Object}
     */
    getChainGems: function() {
        var chainGems = {u:[],d:[],l:[],r:[]};

        // up chain
        var tempGem = null;
        var tempIndex = {r: this.row, c: this.column};
        do {
            tempIndex.r -= 1;
            if (GemUnit.validGemIndex(tempIndex.r, tempIndex.c)) {
                tempGem = GemUnit.allGems[tempIndex.r][tempIndex.c];
                if (this.equals(tempGem)) chainGems.u.push(tempGem);
            } else break;
        } while (this.equals(tempGem));

        // down chain
        tempGem = null;
        tempIndex = {r: this.row, c: this.column};
        do {
            tempIndex.r += 1;
            if (GemUnit.validGemIndex(tempIndex.r, tempIndex.c)) {
                tempGem = GemUnit.allGems[tempIndex.r][tempIndex.c];
                if (this.equals(tempGem)) chainGems.d.push(tempGem);
            } else break;
        } while (this.equals(tempGem));

        // left chain
        tempGem = null;
        tempIndex = {r: this.row, c: this.column};
        do {
            tempIndex.c -= 1;
            if (GemUnit.validGemIndex(tempIndex.r, tempIndex.c)) {
                tempGem = GemUnit.allGems[tempIndex.r][tempIndex.c];
                if (this.equals(tempGem)) chainGems.l.push(tempGem);
            } else break;
        } while (this.equals(tempGem));

        // right chain
        tempGem = null;
        tempIndex = {r: this.row, c: this.column};
        do {
            tempIndex.c += 1;
            if (GemUnit.validGemIndex(tempIndex.r, tempIndex.c)) {
                tempGem = GemUnit.allGems[tempIndex.r][tempIndex.c];
                if (this.equals(tempGem)) chainGems.r.push(tempGem);
            } else break;
        } while (this.equals(tempGem));

        return chainGems;
    }
};